using System;
using System.Collections.Generic;
using igather.Framework;
using System.Text;
using System.Globalization;

namespace igather.Framework.Clustering
{
	/// <summary>
	/// Descibes a group of vertices that are strongly connected within a graph
	/// </summary>
	public class Cluster
	{
		/// <summary>
		/// The list of vertices that are contained in this cluster
		/// </summary>
		public List<Vertex> Vertices { get; protected set; }
	    
		/// <summary>
		/// The list of edges that connect to at least one vertex contained in this cluster
		/// </summary>
	    public List<Edge> Edges { get; protected set; }
	
	    private IGraph mGraph;
	
		/// <summary>
		/// The graph that contains this cluster
		/// </summary>
	    public IGraph Graph 
	    { 
	    	get { return mGraph; } 
	    }
	
		/// <summary>
		/// Adds a vertex to this cluster
		/// </summary>
		/// <param name="v">
		/// The vertex to add to this cluster.
		/// It must be contained in the graph to which this cluster belongs to
		/// </param>
	    public void AddVertex(Vertex v)
	    {
	    	if (Graph.GetVertex(v) == null)
				throw new ArgumentException("Cannot add vertex from a graph outside this cluster");
	
	    	Vertices.Add(v);
	    }
	
		/// <summary>
		/// Removes a vertex from this cluster
		/// </summary>
		/// <param name="v">
		/// The vertex to remove from this cluster
		/// </param>
		/// <returns>
		/// True if the vertex was removed, otherwise false
		/// </returns>
	    public bool RemoveVertex(Vertex v)
	    {
	    	return Vertices.Remove(v);
	    }
	
		/// <summary>
		/// Adds an edge to this cluster
		/// </summary>
		/// <param name="e">
		/// The edge to add.
	    /// It must be contained in the graph this cluster belongs to
		/// </param>
	    public void AddEdge(Edge e)
	    {
	    	if (Graph.GetEdge(e) == null)
				throw new ArgumentException("Cannot add edge from a graph outside this cluster");
	
	    	Edges.Add(e);
	    }
	
		/// <summary>
		/// Removes an edge from this cluster
		/// </summary>
		/// <param name="e">
		/// The edge to remove from this cluster
		/// </param>
		/// <returns>
		/// True if the edge was removed, otherwise false
		/// </returns>
	    public bool RemoveEdge(Edge e)
	    {
	    	return Edges.Remove(e);
	    }
	
		/// <summary>
		/// Default constructor
		/// </summary>
		/// <param name="graph">
		/// The graph to which this cluster belongs to
		/// </param>
	    public Cluster(IGraph graph)
	    {
	    	Vertices = new List<Vertex>();
	    	Edges = new List<Edge>();
	    	mGraph = graph;
	    }

		/// <summary>
		/// Gets the metrics for this cluster
		/// </summary>
		/// <returns>
		/// An object with the quality metrics for this cluster
		/// </returns>
		public Metrics GetMetrics()
		{
			List<Edge> edges = Edges;
			Metrics m = new Metrics();

			foreach (Edge e in edges)
				if ((Vertices.Contains(e.Head)) &&
					(Vertices.Contains(e.Tail)))
					m.Cohesion += e.Weight;
				else
					m.Separation += e.Weight;

			return m;
		}

        /// <summary>
        /// Returns a string that represents a Graphviz file
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            Edge edge;
            StringBuilder returnString = new StringBuilder();

            for (int i = 0; i < Vertices.Count; i++)
            {
                for (int j = i + 1; j < Vertices.Count; j++)
                {
                    edge = mGraph.GetEdge(mGraph.CreateEdge(new Vertex(Vertices[i].Name), new Vertex(Vertices[j].Name)));
                    if (edge != null)
                    {
                        if (edge.Weight != 0)
                            returnString.AppendLine(String.Format("\"{0}\" -- \"{1}\" [label={2}]", Vertices[i].Name, Vertices[j].Name, edge.Weight.ToString(CultureInfo.InvariantCulture)));
                            returnString.AppendLine(String.Format("\"{0}\"[style=filled, fillcolor=<COLOR>]", Vertices[i].Name));
                    }
                }
            }
            return returnString.ToString();
        }
        /// <summary>
        /// gets the most representative vertex from a cluster
        /// </summary>
        /// <returns></returns>
        public Vertex GetRepresentativeVertexByConectivitie()
        {
            Vertex vertex = Vertices[0];
            int maxEdges = Graph.GetEdges(vertex).Count;

            foreach (Vertex v in Vertices)
            {
                if (maxEdges < Graph.GetEdges(v).Count)
                {
                    maxEdges = Graph.GetEdges(v).Count;
                    vertex = v;
                }
            }
            return vertex;
        }

        public Vertex GetRepresentativeVertexBySum()
        {
            List<Edge> edges;
            double sumEdges = 0;
            Vertex maxVertex = null;
            double maxSumEdges = 0;

            foreach (Vertex vertex in Vertices)
            {
                edges = Graph.GetEdges(vertex);
                sumEdges = 0;
                foreach (Edge edge in edges)
                {
                    sumEdges += edge.Weight;
                }
                if (sumEdges > maxSumEdges)
                {
                    maxSumEdges = sumEdges;
                    maxVertex = vertex;
                }
            }
            return maxVertex;
        }
	}
}
